<?php
/*
* Class teia\webdev\php\core\PaginationProvider
* Core class for displaying process stages
*
* @author Bernardo Quibiana
* @date created: 20111202
*
* @date modified: 20111202
* @modified: Bernardo Quibiana
*/

// namespace declaration: Abstract Data Types
namespace teia\webdev\php\core;

require_once 'DisplayableItem.inc';
require_once 'IProcessDataSource.inc';
require_once 'FunctionPool.inc';

class ProcessViewer extends DisplayableItem {

	private $data_source;
	private $current_stage;
	private $cursor = 0;
	private $nav_link = null;
	
	private $auto_nav = false;
	
	private $per_item_content = false;
	private $show_top_nav = true;
	private $show_bottom_nav = false;
	private $show_backward_nav = false;
	private $show_forward_nav = false;
	private $show_stage_link = TRUE;
	private $nav_adjacent_links = PHP_INT_MAX;
	
	private $nav_first_str = '&larr;';
	private $nav_prev_str = '&lt;';
	private $nav_next_str = '&gt;';
	private $nav_last_str = '&rarr;';
	private $nav_jump_str = 'Stage: ';
	
	private $nav_html_class = 'process_stage_nav';
	private $content_html_class = 'process_stage_content';
	private $item_html_class = 'process_stage_item';
	private $stage_html_class = 'process_stage';
	
	/**
	* Enter description here ...
	* @param strign $id
	* @param string $name
	* @param array $var_array
	* @param IPaginationDataSource $data_source
	* @throws \InvalidArgumentException
	*/
	public function __construct($id, $name,  $var_array=NULL, $data_source) {
		parent::__construct($id, $name, $var_array);
	
		if ($data_source instanceof IProcessDataSource) {
			$this->data_source = $data_source;
		} else {
			throw new \InvalidArgumentException('Invalid class for data source, expected: IProcessDataSource', '999');
		}
	}
	
	/**
	 * Set the specified property
	 * @param unknown_type $property name of the property to change (properties ignored: data_source and current_stage)
	 * @param unknown_type $value new value for the specified property
	 */
	public function set_property($property, $value) {
		if ($property != 'data_source' && $property != 'current_stage' && $property != 'cursor') {
			$this->$property = $value;
		}
	}

	public function get_stage_name($cursor) {
		$stages = $this->data_source->get_stages_name();
		$last_stage = abs(count($stages) - 1);
		
		$cursor = FunctionPool::clamp($cursor, 0, $last_stage);
		
		return $stages[$cursor];
	}
	
	public function get_stage_num($stage) {
		$stages = $this->data_source->get_stages_name();
		$cursor = array_search($stage, $stages);
		if ($cursor) {
			return $cursor;
		}
		throw (new \InvalidArgumentException('Undefined stage: ' + $stage, 999));
	}
	
	public function is_first_stage() {
		return ($this->cursor == 0);
	}
	
	public function is_last_stage() {
		$stages = $this->data_source->get_stages_name();
		$last_stage = abs(count($stages) - 1);
		
		return ($this->cursor == $last_stage);
	}
	
	public function display($context='page') {
		// attempt to determine the target stage to display
		if ($this->auto_nav) {
			$this->display_stage($this->cursor);
			$this->cursor = $this->cursor + 1;
			return;
		} elseif (isset($_REQUEST['stage']) && is_string($_REQUEST['stage'])) {
			$cursor = $this->get_stage_num( $_REQUEST['stage']);
		} elseif (isset($_REQUEST['stage_cursor']) && is_numeric($_REQUEST['stage_cursor'])) {
			$cursor = $_REQUEST['stage_cursor'];
		} else {
			$cursor = $this->cursor;
		}
		// display the stage
		$this->display_stage($cursor);
	}
	
	/**
	* Display the specified process stage
	* @param number $cursor index of the stage to display
	*/
	public function display_stage($cursor) {
		echo $this->generate_stage($cursor);
	}
	
	/**
	* Display the previous stage
	*/
	public function display_prev_stage() {
		$this->display_stage($this->cursor - 1);
	}
	
	/**
	 * Display the next stage
	 */
	public function display_next_stage() {
		$this->display_stage($this->cursor + 1);
	}
	
	/**
	* Generate a displayable stage
	* @param number $cursor index of the target stage to display
	* @return string the HTML code to display the targe stage
	*/
	public function generate_stage($cursor) {
		$this->nav_link = null;
		$stages = $this->data_source->get_stages_name();
		$last_stage = abs(count($stages) - 1);
		// clamp requested stage number
		$this->cursor = FunctionPool::clamp($cursor, 0, $last_stage);
		$this->current_stage = $stages[$this->cursor];
		// generate navigation
		$stage_nav = $this->generate_stage_nav($this->cursor, 0, $last_stage, $stages);
		// generate content
		$stage_content = $this->generate_stage_content($this->current_stage);
	
		// code to display
		$content = '<div class="' . $this->stage_html_class . '">' . "\n";
		if ($this->show_top_nav) $content .= $stage_nav;
		$content .= $stage_content;
		if ($this->show_bottom_nav) $content .= $stage_nav;
		$content .= '</div>';
		return $content;
	}
	
	/**
	 * Generate the displayable content for the specified stage
	 * @param string $stage name of target stage to display
	 * @return string the HMTL content to display
	 */
	public function generate_stage_content($stage) {
		$stage_size = $this->data_source->get_stage_size($stage);
		$stage_content = '<div class="' . $this->content_html_class . '">' . "\n";
	
		if ($this->per_item_content) {
			// fetch stage content on per item basis
			for ($i = 0; $i < $stage_size; $i++) {
				$it_content = $this->data_source->get_item_content($i, $stage);
				if ($it_content) {
					$stage_content .= '<div class="' . $this->item_html_class . '">' . "\n" . $it_content . '\n</div>' . "\n";
				}
			}
		} else {
			// fetch stage content on per page basis
			$stage_content = $this->data_source->get_stage_content($stage) . "\n";
		}
		$stage_content .= '</div>' . "\n";
	
		return $stage_content;
	}
	
	/**
	 * Generate the displayble stage navigation
	 * @param number $cursor index of the current stage
	 * @param number $first index of the first stage
	 * @param number $last index of the last stage
	 * @param array $stages array with name of each stage
	 * @return string the HTML navigation code to display
	 */
	public function generate_stage_nav($cursor, $first, $last, $stages) {
	
		$stage_nav = '<div class="' . $this->nav_html_class . '">' . "\n";
		if ($this->show_backward_nav)
			$stage_nav .= $this->generate_backward_nav($curosr, $first);
		$stage_nav .= $this->generate_main_nav($cursor, $first, $last, $stages);
		if ($this->show_forward_nav)
			$stage_nav .= $this->generate_forward_nav($cursor, $last);
		$stage_nav .= '</div>' . "\n";
	
		return $stage_nav;
	}
	
	/**
	 * Generate the stage navigation
	 * @param number $cursor index of the current stage
	 * @param number $first index of the first stage
	 * @param number $last index of the last stage
	 * @param array $stages array with name of each stage
	 * @return string the HTML navigation code to display
	 */
	public function generate_main_nav($cursor, $first, $last, $stages) {
		$content = '';
		$num_links = $cursor - $first;
		if ($num_links > $this->nav_adjacent_links) {
			$content .= '<span class="disabled_stage">...</span>' . "\n";
			$start = $cursor - $this->nav_adjacent_links;
		} else {
			$start = 0;
		}
	
		$content .= $this->generate_named_nav($start, $cursor, $stages, 'prev_stage');
		$content .= '<span class="current_stage">' . $stages[$cursor] . '</span>' . "\n";
	
		$num_links = $last - $cursor;
		if ($num_links > $this->nav_adjacent_links) {
			$limit .= $cursor + $this->nav_adjacent_links + 1;
			$content .= $this->generate_named_nav($cursor + 1, $limit, $stages, 'next_stage');
			$content .= '<span class="disabled_stage">...</span>' . "\n";		
		} else {
			$limit = $last + 1;
			$content .= $this->generate_named_nav($cursor + 1, $limit, $stages, 'next_stage');
		}
	
		return $content;
	}
	
	/**
	 * Generates process navigation status
	 * @param number $start index of the start stage (incluvise)
	 * @param number $limit index of the last stage (excluvise)
	 * @param array $names array with name of each stage
	 * @return string displayable string with stage navigation
	 */
	public function generate_named_nav($start, $limit, $names, $html_class) {
		$content = '';  
		if ($this->show_stage_link) {
			for ($i = $start; $i < $limit; $i++) {
				$link =  $this->generate_nav_link($i);
				$content .= '<a href="'. $link . '">' . $names[$i] . '</a>' . "\n";
			}
		} else {
			for ($i = $start; $i < $limit; $i++) {
				$content .= '<span class="' . $html_class . '">' . $names[$i] . '</span>' . "\n";
			}
		}
		return $content;
	}
	
	/**
	 * Generate the backward (first and previous) navigation links
	 * @param number $cursor index of the current stage
	 * @param number $first index of the first stage
	 * @return string the HTML navigation code to display
	 */
	public function generate_backward_nav($cursor, $first = 0) {
		$prev = $cursor - 1;
		$content = '';
		if ($cursor == $first) {
			// show goto first link
			$content .= '<span class="disabled_stage">' . $this->nav_first_str . '</span>' . "\n";
			// show goto previous link
			$content .= '<span class="disabled_stage">' . $this->nav_prev_str . '</span>' . "\n";
		} else {
			// show goto first link
			$first_link = $this->generate_nav_link($first);
			$content .= '<a href="'. $first_link . '">' . $this->nav_first_str . '</a>' . "\n";
			// show goto previous link
			$prev_link = $this->generate_nav_link($prev);
			$content .= '<a href="'. $prev_link . '">' . $this->nav_prev_str . '</a>' . "\n";
		}
		return $content;
	}
	
	/**
	 * Generate the forward (next and last) navigation links
	 * @param number $cursor index of the current stage
	 * @param number $last index of the last stage
	 * @return string the HTML navigation code to display
	 */
	public function generate_forward_nav($cursor, $last) {
		$next = $cursor + 1;
		$content = '';
		if ($cursor == $last) {
			// show goto next link
			$content .= '<span class="disabled_stage">' . $this->nav_next_str . '</span>' . "\n";
			// show goto last link
			$content .= '<span class="disabled_stage">' . $this->nav_last_str . '</span>' . "\n";
		} else {
			// show goto next link
			$next_link = $this->generate_nav_link($next);
			$content .= '<a href="' . $next_link . '">' . $this->nav_next_str . '</a>' . "\n";
			// show goto last link
			$last_link = $this->generate_nav_link($last);
			$content .= '<a href="'. $last_link . '">' . $this->nav_last_str . '</a>' . "\n";
		}
		return $content;
	}
	
	/**
	 * Generate a stage selection navigation
	 * @param number $cursor index of the current stage
	 * @param number $first index of the first stage
	 * @param number $last index of the last stage
	 * @return string the HTML navigation code to display
	 */
	public function generate_jump_nav($cursor, $first=0, $last, $stages) {
		$content = '';
	
		$link = $this->generate_nav_link('');
		$action = 'window.location="' . $link . '"+this.options[this.selectedIndex].value;';
		$content .= '<label class="stage_nav" for="_select_stage_">' . $this->nav_jump_str . '.</label>' . "\n";
		$content .= '<select id="_select_stage_" class="stage_nav" onchange="' . $action . '">' . "\n";
		// before current stage
		for ($i = $first; $i < $cursor; $i++) {
			$content .= '<option value="' . $i . '">' . $stages[$i]. '</option>' . "\n";
		}
		// current stage
		$content .= '<option value="' . $cursor . '" selected="selected">' . $stages[$cursor] . '</option>' . "\n";
		// after current stage
		for ($i = $cursor + 1; $i <= $last; $i++) {
			$content .= '<option value="' . $i . '">' .  $stages[$i]  . '</option>' . "\n";
		}
		$content .= '</select>' . "\n";
	
		return $content;
	}
	
	/**
	 * Generate the link to a target stage based on the URI of the current request
	 * @param number $cursor index of the target stage
	 * @return string HTML link to target stage
	 */
	public function generate_nav_link($cursor) {
		if (!$this->nav_link) {
			// generates base navigation link based on request URI 
			// "#.*^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#"
			$cursor_pattern = "#\?stage_cursor\=(\d+)#i";
			$name_pattern = '#\?stage\=(\w+)#i';
			if (preg_match($cursor_pattern, $_SERVER['REQUEST_URI'])) {
				// the request contains stage number already, remove it
				// echo 'cursor_pattern: ';
				$request = preg_replace($cursor_pattern, '', $_SERVER['REQUEST_URI']);
			} elseif (preg_match($name_pattern, $_SERVER['REQUEST_URI'])) {
				// the request contains stage name already, remove it
				// echo 'stage_pattern: ';
				$request = preg_replace($name_pattern, '', $_SERVER['REQUEST_URI']);
			} else {
				// the request does not contain stage number nor stage name, use it as-is
				// echo 'no_pattern: ';
				$request = $_SERVER['REQUEST_URI'];
			}
			//echo 'Raw: ' . $_SERVER['REQUEST_URI'] . ' VS Ready: ' . $request . " - $cursor\n";
			$this->nav_link =  $request;
		}
	
		$link = $this->nav_link . "?stage_cursor=$cursor";
	
		return $link;
	}
}
?>
	